/*
Copyright (C) 2007 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

/**
 * @fileoverview Class and structures for handling UI and behaviour of a clock.
 *     The code used in this file is IE specific.
 */

/**
 * Class modeling an analog clock.
 * @param {Object} container A view or div that supports appendElement method
 *     in which to add the clock's ui elements.
 * @param {Number} designId The id of the design to use for drawing the clock.
 * @param {Number} backgroundId The id of the background to use.
 * @param {Number} offsetMinutes Offset in minutes from GMT.
 * @param {String} locationName The text to be writen under the clock as it's
 *     location name.
 * @param {Boolean} showSeconds Determines if a seconds handle will be drawn
 *     and handled (only if the design supports seconds handles).
 * @param {Boolean} isEditOnClick If true, an edit clock dialog will be shown
 *     when this clock is duble-clicked on.
 * @constructor
 * @requires CLOCK_INF
 */
function Clock(container, designId, backgroundId, offsetMinutes, locationName,
               showSeconds, isEditOnClick) {
  var design = CLOCK_INF.DESIGNS[designId];

  /**
   * Stores this clock's design id.
   * @type Number
   */
  this.designId = designId;

  /**
   * Stores this clock's background id.
   * @type Number
   */

  this.backgroundId = backgroundId;

  /**
   * A prefix that will be added to the path of all images.
   * @type String
   */
  var urlPrefix = 'images/design' + (designId + 1) + '/';

  // There can be a maximum of 12 hours = 720 minutes offset from GMT:
  while (offsetMinutes > HALF_A_DAY) {
    offsetMinutes -= HALF_A_DAY;
  }
  while (offsetMinutes < -HALF_A_DAY) {
    offsetMinutes += HALF_A_DAY;
  }

  /**
   * Stores this clock's offset in minutes from GMT.
   * @type Number
   */
  this.offsetMinutes = offsetMinutes;

  /**
   * Reference to the div UI element containing the clock.
   * @type Object
   */
  this.clockDiv_ = container.appendElement(
      '<div x="0" y="0" width="' + CLOCK_INF.ORIGINAL_WIDTH +
      '" height="' + CLOCK_INF.ORIGINAL_HEIGHT + '"></div>');

  /**
   * Reference to the image UI element of the background.
   * @type Object
   */
  this.baseImage_ = this.clockDiv_.appendElement(
      '<img src="images/backgrounds/back' + (backgroundId + 1) +
      '.png" width="100%" height="100%" />');

  /**
   * Reference to the image UI element of the clock face.
   * @type Object
   */
  this.clockFace_ = this.clockDiv_.appendElement(
      '<img src="' + urlPrefix + 'clock_face.png" width="100%"/>');

  /**
   * Reference to the image UI element of the hour handle.
   * @type Object
   */
  this.hourHand_ = this.clockDiv_.appendElement(
      '<img src="' + urlPrefix + 'hourhand.png"' + ' x="' +
      design.FACE_CENTER_X + '" y="' +
      design.FACE_CENTER_X + '" ' + 'pinX="' +
      design.HOUR_PIN_X + '" pinY="' +
      design.HOUR_PIN_Y + '" />');

  /**
   * Reference to the image UI element of the minute handle.
   * @type Object
   */
  this.minuteHand_ = this.clockDiv_.appendElement(
      '<img src="' + urlPrefix + 'minutehand.png" x="' +
      design.FACE_CENTER_X + '" y="' +
      design.FACE_CENTER_X + '" ' + 'pinX="' +
      design.MINUTE_PIN_X + '" pinY="' +
      design.MINUTE_PIN_Y + '" />');

  /**
   * Reference to the image UI element of the second handle.
   * @type Object
   */
  this.secondHand_ = null;
  if (design.SECOND_PIN_X !== null) {
    this.secondHand_ = this.clockDiv_.appendElement(
        '<img src="' + urlPrefix + 'secondhand.png" x="' +
        design.FACE_CENTER_X + '" y="' +
        design.FACE_CENTER_X + '" ' + 'pinX="' +
        design.SECOND_PIN_X + '" piny="' +
        design.SECOND_PIN_Y + '" visible="' +
        showSeconds + '"/>');
  }

  /**
   * Reference to the image UI element of the center-piece.
   * @type Object
   */
  this.centerPin_ = this.clockDiv_.appendElement(
      '<img src="' + urlPrefix + 'center.png" x="' +
      design.CENTER_X + '" y="' +
      design.CENTER_Y + '"/>');

  /**
   * Reference to the label UI element of the location name.
   * @type Object
   */
  this.cityLabel = this.clockDiv_.appendElement(
      '<label align="center" vAlign="middle" width="100%" height="20" ' +
      'y="127" trimming="character-ellipsis" font="Arial" size="12" ' +
      'bold="true" color="#' + (backgroundId > -1 ? 'FFFFFF' : '000000') +
      '" />');
  this.cityLabel.innerText = locationName;

  /**
   * Reference to the button UI element of the event area. It is invisible
   * and handles all mouse interactions with the clock, as well as display.
   * @type Object
   */
  this.eventArea = this.clockDiv_.appendElement(
      '<button x="0" y="0" width="100%" height="100%" />');
  if (isEditOnClick) {
    this.eventArea.ondblclick = Clock.editClock;
  }
  this.eventArea.tooltip = '\x00';
}

/**
 * Moves the clock to the specified coordinates relative to the top left corner.
 * @param {Number} x The horizontal space between the clock and the
 *     leftmost pixel of the view.
 * @param {Number} y The vertical space between the clock and the top
 *     of the view.
 */
Clock.prototype.moveTo = function(x, y) {
  this.clockDiv_.x = x;
  this.clockDiv_.y = y;
};

/**
 * Cleans up the UI of a clock. Called before a clock is destroyed.
 */
Clock.prototype.destroyClock = function() {
  this.clockDiv_.removeAllElements();
  removeElement(this.clockDiv_);
};

/**
 * Spins the seconds handle of this clock by 6 degrees (1 second).
 */
Clock.prototype.updateSecondHand = function() {
  if (this.secondHand_ === null) {
    return;
  }
  this.secondHand_.rotation = (this.secondHand_.rotation + 6) % 360;
};

/**
 * Sets this clock to an indicated time.
 * @param {Date} now The time this clock will indicate.
 * @param {Boolean} showTooltip Indicates whether to add a tooltip of the time.
 * @returns the current time as a {@link Time} structure if showTooltip is true.
 * @requires Time
 */
Clock.prototype.setTime = function(now, showTooltip) {
  now.setMonth(0);  // Avoid getting daylight timezones.
  if (this.secondHand_ !== null) {
    this.secondHand_.rotation = now.getUTCSeconds() * 6;
  }
  this.minuteHand_.rotation = (now.getUTCMinutes() +
      this.offsetMinutes % 60) * 6 + now.getUTCSeconds() / 10;
  this.hourHand_.rotation = now.getUTCHours() * 30 +
      (this.offsetMinutes + now.getUTCMinutes()) / 2;
  if (showTooltip) {
    /**
     * Holds the hours of the time this clock is currently indicating.
     * @type Number
     */
    var hours = (now.getUTCHours() +
        Math.floor((now.getUTCMinutes() + this.offsetMinutes) / 60) + 24) % 24;

    /**
     * Holds the minutes of the time this clock is currently indicating.
     * @type Number
     */
    var minutes = Math.floor(now.getUTCMinutes() +
                  this.offsetMinutes + HALF_A_DAY) % 60;

    /**
     * Stores the current time to be returned, before we make some changes to it
     * that are required for printing it on the screen in a tooltip.
     * @type Time
     */
    var clockTime = new Time(hours, minutes);
    if (hours < 10) {
      hours = '0' + hours;
    }
    if (minutes < 10) {
      minutes = '0' + minutes;
    }

    this.eventArea.tooltip = this.cityLabel.innerText + ' - ' + hours + ':' +
                             minutes;
    // Return the time the clock is set to. This is only needed in
    // the add a clock dialog.
    return clockTime;
  }
};

/**
 * Zooms this clock to fit in a rectangle of width by height.
 * @param {Number} width The width the clock will fit into by zooming.
 * @param {Number} height The height the clock will fit into by zooming.
 * @requires CLOCK_INF
 */
Clock.prototype.zoom = function(width, height) {
  /**
   * All width, height, x and y values bellow are set in the following way :
   * new element width (or x) = new div width * old element width (or x) /
   *                            old div width
   * This is because :
   *  old element width / new element width == old div width / new div width
   *
   * For height (or y) it's the same way; both old div width and height reffer
   * to the size of the div without the lower part for the name; the new div
   * width will be equal to width which is changed by the object calling
   * the zoom function of a clock object.
   */

  var design = CLOCK_INF.DESIGNS[this.designId];
  this.clockDiv_.width = this.clockFace_.height = width;
  this.clockDiv_.height = height;
  this.hourHand_.width = width * design.HOUR_W / CLOCK_INF.ORIGINAL_WIDTH;
  this.hourHand_.height = width * design.HOUR_H / CLOCK_INF.ORIGINAL_WIDTH;
  this.hourHand_.pinX = width * design.HOUR_PIN_X / CLOCK_INF.ORIGINAL_WIDTH;
  this.hourHand_.pinY = width * design.HOUR_PIN_Y / CLOCK_INF.ORIGINAL_WIDTH;
  this.minuteHand_.width = width * design.MINUTE_W / CLOCK_INF.ORIGINAL_WIDTH;
  this.minuteHand_.height = width * design.MINUTE_H / CLOCK_INF.ORIGINAL_WIDTH;
  this.minuteHand_.pinX = width * design.MINUTE_PIN_X /
      CLOCK_INF.ORIGINAL_WIDTH;
  this.minuteHand_.pinY = width * design.MINUTE_PIN_Y /
      CLOCK_INF.ORIGINAL_WIDTH;
  this.hourHand_.x = this.minuteHand_.x = width * design.FACE_CENTER_X /
      CLOCK_INF.ORIGINAL_WIDTH;
  this.hourHand_.y = this.minuteHand_.y = width * design.FACE_CENTER_Y /
      CLOCK_INF.ORIGINAL_WIDTH;

  if (this.secondHand_ !== null) {
    this.secondHand_.width = width * design.SECOND_W /
        CLOCK_INF.ORIGINAL_WIDTH;
    this.secondHand_.height = width * design.SECOND_H /
        CLOCK_INF.ORIGINAL_WIDTH;
    this.secondHand_.pinX = width * design.SECOND_PIN_X /
        CLOCK_INF.ORIGINAL_WIDTH;
    this.secondHand_.pinY = width * design.SECOND_PIN_Y /
        CLOCK_INF.ORIGINAL_WIDTH;
    this.secondHand_.x = this.hourHand_.x;
    this.secondHand_.y = this.hourHand_.y;
  }

  this.centerPin_.width = width * design.CENTER_W / CLOCK_INF.ORIGINAL_WIDTH;
  this.centerPin_.height = width * design.CENTER_H / CLOCK_INF.ORIGINAL_WIDTH;
  this.centerPin_.x = width * design.CENTER_X / CLOCK_INF.ORIGINAL_WIDTH;
  this.centerPin_.y = width * design.CENTER_Y / CLOCK_INF.ORIGINAL_WIDTH;
  this.cityLabel.height = height * 20 / CLOCK_INF.ORIGINAL_HEIGHT;
  this.cityLabel.y = width + 2;
  this.cityLabel.size = width * 12 / CLOCK_INF.ORIGINAL_WIDTH;
};

/**
 * Called when a clock is double-clicked on. It opens the edit dialog for
 * this clock.
 * @requires Menus
 */
Clock.editClock = function() {
  if (tatwGadget.clockPanel.focusedClock > -1) {
    tatwGadget.menus.editClock(tatwGadget.clockPanel.focusedClock);
    tatwGadget.clockPanel.focusedClock = -1;
  }
};

/**
 * Structure for holding a time.
 * @param {Number} hours The time's hour value.
 * @param {Number} minutes The time's minutes value.
 * @constructor
 */
function Time(hours, minutes) {
  this.hours = hours;
  this.minutes = minutes;
}
